package com.flyqiu.common.http;

import cn.hutool.http.*;
import com.alibaba.fastjson2.JSONObject;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.function.Function;

/**
 * HTTP 工具类，提供各种 HTTP 请求相关的功能。
 */
public class FlyQiuHttpTools {

    private static final Function<String, JSONObject> toJson = JSONObject::parseObject;

    /**
     * 发送 POST 请求并返回字符串结果。
     *
     * @param url    请求 URL
     * @param params 请求参数
     * @return 响应体字符串
     */
    public static String postResultStr(String url, Map<String, Object> params) {
        return postResultStr(url, params, null);
    }

    /**
     * 发送 POST 请求并返回字符串结果。
     *
     * @param url     请求 URL
     * @param headers 请求头
     * @param params  请求参数
     * @return 响应体字符串
     */
    public static String postResultStr(String url, Map<String, Object> params, Map<String, Object> headers) {
        HttpRequest post = createHttpRequest(url, Method.POST, params, headers);
        HttpResponse response = post.execute();
        if (!response.isOk()) {
            throw new HttpException("请求失败:" + url);
        }
        return response.body();
    }


    /**
     * 发送 POST 请求并返回 JSON 对象。
     *
     * @param url    请求 URL
     * @param params 请求参数
     * @return 响应体 JSON 对象
     */
    public static JSONObject postResultJson(String url, Map<String, Object> params) {
        return postResultObject(url, params, null, toJson);
    }

    /**
     * 发送 POST 请求并返回 JSON 对象。
     *
     * @param url    请求 URL
     * @param params 请求参数
     * @param header 请求头
     * @return 响应体 JSON 对象
     */
    public static JSONObject postResultJson(String url, Map<String, Object> params, Map<String, Object> header) {
        return postResultObject(url, params, header, toJson);
    }


    /**
     * 发送 POST 请求并返回指定类型的对象。
     *
     * @param url           请求 URL
     * @param params        请求参数
     * @param resultMapping 结果映射函数
     * @param <R>           返回类型
     * @return 响应体对象
     */
    public static <R> R postResultObject(String url, Map<String, Object> params, Map<String, Object> header, Function<String, R> resultMapping) {
        String resultStr = postResultStr(url, params, header);
        return resultMapping.apply(resultStr);
    }


    /**
     * 发送 GET 请求并返回 JSON 对象。
     *
     * @param url    请求 URL
     * @param params 请求参数
     * @return 响应体 JSON 对象
     */
    public static JSONObject getResultJson(String url, Map<String, Object> params) {
        return getResultJson(url, params, null);
    }

    /**
     * 发送 GET 请求并返回 JSON 对象。
     *
     * @param url     请求 URL
     * @param params  请求参数
     * @param headers 请求头
     * @return 响应体 JSON 对象
     */
    public static JSONObject getResultJson(String url, Map<String, Object> params, Map<String, Object> headers) {
        return getResultObject(url, params, headers, JSONObject::parseObject);
    }

    /**
     * 发送 GET 请求并返回指定类型的对象。
     *
     * @param url           请求 URL
     * @param params        请求参数
     * @param resultMapping 结果映射函数
     * @param <R>           返回类型
     * @return 响应体对象
     */
    public static <R> R getResultObject(String url, Map<String, Object> params, Function<String, R> resultMapping) {
        return getResultObject(url, params, null, resultMapping);
    }

    /**
     * 发送 GET 请求并返回指定类型的对象。
     *
     * @param url           请求 URL
     * @param params        请求参数
     * @param headers       请求头
     * @param resultMapping 结果映射函数
     * @param <R>           返回类型
     * @return 响应体对象
     */
    public static <R> R getResultObject(String url, Map<String, Object> params, Map<String, Object> headers, Function<String, R> resultMapping) {
        String resultStr = getResultStr(url, params, headers);
        return convertToObject(resultStr, resultMapping);
    }

    /**
     * 发送 GET 请求并返回字符串结果。
     *
     * @param url     请求 URL
     * @param params  请求参数
     * @param headers 请求头
     * @return 响应体字符串
     */
    public static String getResultStr(String url, Map<String, Object> params, Map<String, Object> headers) {
        HttpRequest request = createHttpRequest(url, Method.GET, params, headers);
        HttpResponse response = request.execute();
        if (!response.isOk()) {
            throw new HttpException("请求失败:" + url);
        }
        return response.body();
    }

    /**
     * 创建HTTP请求
     *
     * @param url 请求的URL
     * @param httpMethod HTTP方法（GET、POST、PUT等）
     * @param params 请求参数
     * @param headers 请求头
     * @return 返回构建的HttpRequest对象
     */
    public static HttpRequest createHttpRequest(String url, Method httpMethod, Map<String, Object> params, Map<String, Object> headers) {
        // 如果未指定HTTP方法，则默认使用GET方法
        if (httpMethod == null) {
            httpMethod = Method.GET;
        }
        HttpRequest request;

        // 根据HTTP方法创建请求
        switch (httpMethod) {
            case POST:
            case PUT:
                // 对于POST和PUT方法，创建请求并设置请求体
                request = HttpUtil.createRequest(httpMethod, url);
                request.body(JSONObject.from(params).toJSONString());
                break;
            default:
                // 对于其他HTTP方法，如果存在请求参数，则将其附加到URL上
                if (FlyQiuCollectionTools.isNotEmpty(params)) {
                    String urlParams = HttpUtil.toParams(params);
                    if (url.indexOf("?") > 0) {
                        url = url + "&" + urlParams;
                    } else {
                        url = url + "?" + urlParams;
                    }
                }
                // 创建请求
                request = HttpUtil.createRequest(httpMethod, url);
        }

        // 设置请求头
        FlyQiuCollectionTools.iterator(headers, (head, object) -> request.header(head, String.valueOf(object)));
        return request;
    }

    /**
     * 创建HTTP请求
     *
     * @param url 请求的URL
     * @param httpMethod HTTP方法（GET、POST、PUT等）
     * @param params 请求参数
     * @param headers 请求头
     * @return 返回构建的HttpRequest对象
     */
    public static HttpRequest createHttpRequest(String url, String httpMethod, Map<String, Object> params, Map<String, Object> headers) {
        // 如果未指定HTTP方法，则默认使用GET方法
        if (StringUtils.isBlank(httpMethod)) {
            httpMethod = "get";
        }
        HttpRequest request;
        // 根据字符串形式的HTTP方法转换为枚举形式
        Method method = getMethod(httpMethod);

        // 如果是GET方法且存在请求参数，则将其附加到URL上
        if (method == Method.GET && FlyQiuCollectionTools.isNotEmpty(params)) {
            String urlParams = HttpUtil.toParams(params);
            if (url.indexOf("?") > 0) {
                url = url + "&" + urlParams;
            } else {
                url = url + "?" + urlParams;
            }
        }

        // 创建请求
        request = HttpUtil.createRequest(method, url);
        // 设置请求头
        FlyQiuCollectionTools.iterator(headers, (head, object) -> request.header(head, String.valueOf(object)));
        return request;
    }

    /**
     * 根据字符串形式的HTTP方法获取对应的枚举值
     *
     * @param method 字符串形式的HTTP方法
     * @return 对应的Method枚举值
     * @throws IllegalArgumentException 如果传入的HTTP方法无效
     */
    public static Method getMethod(String method) {
        return switch (method.toUpperCase()) {
            case "GET" -> Method.GET;
            case "POST" -> Method.POST;
            case "PUT" -> Method.PUT;
            case "DELETE" -> Method.DELETE;
            case "HEAD" -> Method.HEAD;
            case "OPTIONS" -> Method.OPTIONS;
            case "TRACE" -> Method.TRACE;
            case "PATCH" -> Method.PATCH;
            default -> throw new IllegalArgumentException("Invalid HTTP method: " + method);
        };
    }


    /**
     * 将字符串结果转换为指定类型的对象。
     *
     * @param resultStr 结果字符串
     * @param function  结果映射函数
     * @param <R>       返回类型
     * @return 转换后的对象
     */
    private static <R> R convertToObject(String resultStr, Function<String, R> function) {
        try {
            return function.apply(resultStr);
        } catch (Exception e) {
            throw new RuntimeException("数据类型转换错误：" + resultStr + "\n" + e.getMessage());
        }
    }
}